package jp.co.apl.haven_enquete.android.dao;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import jp.co.apl.haven.generated.HavenProtos.Category;
import jp.co.apl.haven.generated.HavenProtos.Enquete;
import jp.co.apl.haven.generated.HavenProtos.Question;
import jp.co.apl.haven.generated.HavenProtos.SelectionItem;
import jp.dev7.android.exception.ApplicationException;
import android.content.Context;
import android.util.Log;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class QuestionDataDao {

    public static final String FILE_NAME = "question.dat";

    private Context context;

    public QuestionDataDao(Context context) {
        this.context = context;
    }

    /**
     * クラスパス内のquestion.datをファイルに書き出す.
     * 
     * @throws IOException
     */
    public void initDataFile() {
        Log.d("haven", "create question.dat start");

        BufferedInputStream src = null;
        FileOutputStream out = null;
        try {
            File dest = new File(FILE_NAME);
            if (dest.exists()) {
                dest.delete();
            }
            src =
                new BufferedInputStream(getClass().getResourceAsStream(
                    "/" + FILE_NAME));
            out = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
            byte[] buf = new byte[1024];
            int sz;
            while ((sz = src.read(buf)) != -1) {
                out.write(buf, 0, sz);
            }
            out.flush();

        } catch (IOException e) {
            throw new ApplicationException(e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
            }
            try {
                if (src != null) {
                    src.close();
                }
            } catch (Exception e) {
            }
            Log.d("haven", "create question.dat end");
        }
    }

    public boolean isExistsDataFile() {
        return Arrays.asList(context.fileList()).contains(FILE_NAME);
    }

    public Enquete load() {
        Log.d("haven", "data load start");
        try {

            if (!isFileExists()) {
                initDataFile();
            }
            return Enquete.parseFrom(context.openFileInput(FILE_NAME));

        } catch (Exception e) {
            throw new ApplicationException(e);
        } finally {
            Log.d("haven", "data load end");
        }
    }

    private boolean isFileExists() {
        return Arrays.asList(context.fileList()).contains(FILE_NAME);
    }

    public Map<Integer, Question> loadAsMap() {
        Log.d("haven", "data loadAsMap start");
        try {
            final Map<Integer, Question> result = Maps.newLinkedHashMap();

            for (Category category : load().getCategoryList()) {
                putQuestionToMap(result, category.getQuestionList());
            }

            return result;
        } finally {
            Log.d("haven", "data loadAsMap end");
        }
    }

    private void putQuestionToMap(final Map<Integer, Question> map,
            List<Question> questionMaps) {
        for (Question q : questionMaps) {

            map.put(q.getId(), q);

            for (SelectionItem item : q.getSelectionItemList()) {
                putQuestionToMap(map, item.getChildQuestionList());
            }
        }
    }

    public List<Question> loadAsList() {
        Log.d("haven", "data loadAsList start");
        try {
            final List<Question> result = Lists.newArrayList();

            for (Category category : load().getCategoryList()) {
                for (Question q : category.getQuestionList()) {

                    result.add(q);

                    for (SelectionItem item : q.getSelectionItemList()) {
                        addQuestionToList(result, item.getChildQuestionList());
                    }
                }
            }

            return result;
        } finally {
            Log.d("haven", "data loadAsList end");
        }
    }

    private void addQuestionToList(List<Question> list, List<Question> questions) {
        for (Question q : questions) {

            list.add(q);

            for (SelectionItem item : q.getSelectionItemList()) {
                addQuestionToList(list, item.getChildQuestionList());
            }
        }
    }

    public List<Category> loadCategories() {
        Log.d("haven", "data loadCategories start");
        try {
            return load().getCategoryList();
        } finally {
            Log.d("haven", "data loadCategories end");
        }
    }

    public List<Question> loadAsListTopLevelOnly() {
        final List<Question> result = Lists.newArrayList();

        for (Category c : load().getCategoryList()) {
            result.addAll(c.getQuestionList());
        }

        return result;
    }

    public int getPrevQuestionId(int questionId) {
        Log.d("haven", "getPrevQuestionId start id=" + questionId);
        int result = -1;
        try {

            int top = -1;
            for (Category category : load().getCategoryList()) {
                for (Question q : category.getQuestionList()) {
                    if (questionId == q.getId()) {
                        return result;
                    } else {
                        result = q.getId();
                    }
                    top = result;
                    for (SelectionItem sel : q.getSelectionItemList()) {
                        for (Question cq : sel.getChildQuestionList()) {
                            if (questionId == cq.getId()) {
                                return result;
                            } else {
                                result = cq.getId();
                            }
                        }
                        result = top;
                    }
                }
            }

            return result;
        } finally {
            Log.d("haven", "getPrevQuestionId end =" + result);
        }
    }

    public int getNextQuestionId(int questionId) {
        Log.d("haven", "getNextQuestionId start id=" + questionId);
        int result = -1;
        try {

            boolean top = false;
            boolean found = false;
            for (Category category : load().getCategoryList()) {
                for (Question q : category.getQuestionList()) {
                    if (found) {
                        result = q.getId();
                        return result;
                    } else if (questionId == q.getId()) {
                        top = true;
                        found = true;
                    }
                    if (!top) {
                        for (SelectionItem sel : q.getSelectionItemList()) {
                            for (Question cqmap : sel.getChildQuestionList()) {
                                if (found) {
                                    result = cqmap.getId();
                                    return result;
                                } else if (questionId == cqmap.getId()) {
                                    found = true;
                                }
                            }
                            if (found) {
                                break;
                            }
                        }
                    }
                }
            }

            return result;
        } finally {
            Log.d("haven", "getNextQuestionId end =" + result);
        }
    }

    public List<Question> flat(List<Question> questionList) {
        List<Question> questions = Lists.newArrayList();
        for (Question q : questionList) {
            questions.add(q);
            for (SelectionItem sel : q.getSelectionItemList()) {
                questions.addAll(sel.getChildQuestionList());
            }
        }
        return questions;
    }

    public List<Question> loadAsListByCategoryId(int id) {
        for (Category category : load().getCategoryList()) {
            if (category.getId() == id) {
                return flat(category.getQuestionList());
            }
        }
        return Collections.emptyList();
    }

    public Category getCategory(int categoryId) {
        for (Category category : load().getCategoryList()) {
            if (category.getId() == categoryId) {
                return category;
            }
        }
        return null;
    }

}
